/*
 * Copyright (c) Huawei Technologies Co., Ltd. 2021-2022. All rights reserved.
 *
 */

import * as fs from 'fs-extra';

import { LegacyAbilityModel } from '../../model/ability/legacy-ability-model.js';
import { ModuleBuildProfile } from '../../options/build/module-build-profile.js';
import { ConfigJson } from '../../options/configure/config-json-options.js';
import { OhosLogger } from '../../utils/log/ohos-logger.js';
import { TaskNames } from '../common/task-names.js';
import { TargetTaskService } from '../service/target-task-service.js';
import { OhosHapTask } from '../task/ohos-hap-task.js';

import { LegacyMergeProfile } from './legacy-merge-profile.js';
import LegacyFATask = TaskNames.LegacyFATask;
import ConfigOptObj = ConfigJson.ConfigOptObj;
import { TaskInputValue } from '@ohos/hvigor';
import { FileSet } from '@ohos/hvigor';
import { hvigorCore } from '@ohos/hvigor';

import { CommonConst } from '../../const/common-const.js';
import { DeviceTypeConst } from '../../const/device-type-const.js';
import { LegacyModuleModelImpl } from '../../model/module/legacy-module-model-impl.js';
import { getJson5Obj } from '../../utils/json-util.js';

const INPUT_KEY = {
  arkEnable: 'arkEnable',
  compileMode: 'compileMode',
  deviceTypes: 'deviceTypes',
  targetName: 'targetName',
  isDebug: 'isDebug',
};

/**
 * 处理config.json
 *
 * @since 2022/1/10
 */
export class LegacyProcessProfile extends OhosHapTask {
  private _log: OhosLogger = OhosLogger.getLogger(LegacyProcessProfile.name);
  private readonly _arkEnable: boolean;
  private readonly _deviceTypes: string[];
  private readonly _intermediatesMergeLegacyProfile: string;
  private readonly _processedModuleJson: string;
  private readonly _moduleBuildProfilePath: string;

  constructor(taskService: TargetTaskService) {
    super(taskService, LegacyFATask.PROCESS_PROFILE);
    this._arkEnable = this.service.isArkModule();
    this._deviceTypes = this.targetData.getTargetDeviceType();
    this._intermediatesMergeLegacyProfile = this.pathInfo.getIntermediatesMergeLegacyProfile();
    this._processedModuleJson = this.pathInfo.getIntermediatesProcessLegacyProfile();
    this._moduleBuildProfilePath = this.moduleModel.getProfilePath();
  }

  initTaskDepends() {
    this.declareDepends(LegacyMergeProfile.name);
  }

  doTaskAction(): void {
    const mergedConfigOpt: ConfigOptObj = getJson5Obj(this._intermediatesMergeLegacyProfile);
    if (this._arkEnable) {
      const distroObj = mergedConfigOpt.module.distro;
      if (distroObj !== undefined) {
        const compatibleApiVersion = this.projectModel.getCompatibleApiMetaByProduct(this.targetData.getProduct().name).version;
        if (!(this._deviceTypes.includes(DeviceTypeConst.LITE_WEARABLE) || this._deviceTypes.includes(DeviceTypeConst.SMART_VISION))) {
          mergedConfigOpt.module.distro.virtualMachine = `ark${this.sdkInfo.getArkVersion(compatibleApiVersion, this.compatibleSdkVersionStage)}`;
        }
      }
    }

    // 根据module target里的配置替换config.json里的js的page, 如果没有对应ability和对应page则提示
    const legacyAbilities = (this.moduleModel as LegacyModuleModelImpl).getLegacyAbilities(this.targetData.getTargetName());
    this.validateTargetAbilityName(this.targetData.getTargetOpt(), legacyAbilities);
    const targetConfigOpts = legacyAbilities.map((legacyAbility) => this.targetData.findTargetConfigOpt(legacyAbility));
    this.replaceTargetAbilityPages(targetConfigOpts, mergedConfigOpt);

    // 替换target中配置的deviceType
    mergedConfigOpt.module.deviceType = this._deviceTypes;

    // 替换target中配置的DistroFilter
    const targetDistroFilter = this.targetData.getTargetOpt().config?.distroFilter;
    if (targetDistroFilter) {
      mergedConfigOpt.module.distroFilter = targetDistroFilter;
    }

    // 当模块是跨设备，target包含富设备时，调用restool编译时需在config.json中补齐package及skills字段
    if (this.moduleModel.isMixedDeviceModule() && this.targetData.hasRichDeviceInTarget()) {
      mergedConfigOpt.module.package = CommonConst.DEFAULT_PACKAGE_NAME;
      if (mergedConfigOpt.module.abilities?.length) {
        // 需保留用户自己写的skills
        if (mergedConfigOpt.module.abilities[0].skills?.length) {
          mergedConfigOpt.module.abilities[0].skills = [
            ...mergedConfigOpt.module.abilities[0].skills,
            {
              entities: ['entity.system.home'],
              actions: ['action.system.home'],
            },
          ];
        } else {
          mergedConfigOpt.module.abilities[0].skills = [{ entities: ['entity.system.home'], actions: ['action.system.home'] }];
        }
      }
    }

    fs.outputJSONSync(this._processedModuleJson, mergedConfigOpt);
  }

  private replaceTargetAbilityPages(targetConfigOpts: (ModuleBuildProfile.AbilityBuildOpt | undefined)[], mergedConfigOpt: ConfigJson.ConfigOptObj) {
    for (const targetConfigOpt of targetConfigOpts) {
      if (targetConfigOpt === undefined) {
        continue;
      }

      mergedConfigOpt.module.js?.forEach((jsOpt) => {
        if (jsOpt.name === targetConfigOpt!.name) {
          this.replaceAbilityPage(jsOpt, targetConfigOpt!);
        }
      });
    }
  }

  private replaceAbilityPage(jsOpt: ConfigJson.JsObj, targetConfigOpts: ModuleBuildProfile.AbilityBuildOpt): void {
    if (targetConfigOpts.pages === undefined) {
      return;
    }

    const invalidTargetPage = targetConfigOpts.pages.filter((targetPage) => !jsOpt.pages.includes(targetPage));
    if (invalidTargetPage.length === 0) {
      jsOpt.pages = targetConfigOpts.pages;
    } else {
      this.printInvalidError('page', invalidTargetPage.join(','));
    }
  }

  private validateTargetAbilityName(targetOpt: ModuleBuildProfile.ModuleTargetBuildOpt, legacyAbilities: LegacyAbilityModel[]) {
    const invalidAbilityNames = targetOpt?.source?.abilities
      ?.filter((targetAbility) => {
        return !legacyAbilities.some((legacyAbility) => {
          return targetAbility.name === legacyAbility.getName();
        });
      })
      .map((ability) => ability.name);

    if (invalidAbilityNames && invalidAbilityNames.length > 0) {
      this.printInvalidError('ability', invalidAbilityNames.join(','));
      return;
    }

    const invalidAbilityType = targetOpt?.source?.abilities
      ?.filter((targetAbility) => {
        return !legacyAbilities.some((legacyAbility) => {
          return targetAbility.name === legacyAbility.getName() && legacyAbility.getType() === 'page';
        });
      })
      .map((ability) => ability.name);

    if (invalidAbilityType && invalidAbilityType.length > 0) {
      this.printInvalidError('ability type, not page ability', invalidAbilityType.join(','));
    }
  }

  private printInvalidError(targetContent: string, invalidStrings: string) {
    this._log.printErrorExit(
      'INVALID_CONFIG_UNDER_TARGET',
      [targetContent, this.moduleModel.getProfilePath()],
      [[invalidStrings]]
    );
  }

  declareInputs(): Map<string, TaskInputValue> {
    const inputs = new Map<string, TaskInputValue>();
    inputs.set(INPUT_KEY.arkEnable, this._arkEnable);
    inputs.set(INPUT_KEY.deviceTypes, this._deviceTypes);
    inputs.set(INPUT_KEY.isDebug, this.targetService.isDebug());
    inputs.set('compatibleSdkVersionStage', this.compatibleSdkVersionStage || '');
    return inputs;
  }

  declareInputFiles(): FileSet {
    const inputFiles = new FileSet();
    inputFiles.addEntry(this._intermediatesMergeLegacyProfile);
    inputFiles.addEntry(this._moduleBuildProfilePath);
    return inputFiles;
  }

  declareOutputFiles(): FileSet {
    return new FileSet().addEntry(this._processedModuleJson);
  }
}
